#include "ChatService.hpp"
#include "public.hpp"
#include "OfflineMessageModel.hpp"
#include <muduo/base/Logging.h>
// 把实例对象写为静态类型这样子还不会有线程安全的问题
ChatService *ChatService::instance()
{
    // 调用构造函数生成唯一实例
    static ChatService megType;
    // 返回static对象
    return &megType;
}

handler ChatService::getHandler(int messageid)
{
    auto it = _megHandlerMap.find(messageid);
    // 关于这个怎么处理的等下再看看
    if (it == _megHandlerMap.end())
    {
        // 下面是我的处理方法 ， 这种处理方法有点挫了
        //    LOG_ERROR << "The messageid is not find in MessageHandlerMap";
        //    return;
        // 使用lambda表达式处理
        return [=](const muduo::net::TcpConnectionPtr &conn, json &js, muduo::Timestamp time)
        {
            LOG_ERROR << "The messageid is : " << messageid << " not find in MessageHandlerMap";
        };
    }

    return _megHandlerMap[messageid];
}

ChatService::ChatService()
{
    //_MegHandlerMap.insert(std::make_pair(LOGIN_MSG, std::bind(&ChatService::Login, this, _1, _2, _3)));
    //_MegHandlerMap.insert(std::make_pair(REG_MSG, std::bind(&ChatService::Register, this, _1, _2, _3)));

    _megHandlerMap.insert({LOGIN_MSG, std::bind(&ChatService::Login, this, _1, _2, _3)});
    _megHandlerMap.insert({REG_MSG, std::bind(&ChatService::Register, this, _1, _2, _3)});
    _megHandlerMap.insert({ONE_CHAT_MSG, std::bind(&ChatService::OneChat, this, _1, _2, _3)});

    _megHandlerMap.insert({ADD_FERIEND_MSG, std::bind(&ChatService::AddFriend, this, _1, _2, _3)});

}

void ChatService::Login(const muduo::net::TcpConnectionPtr &conn, json &js, muduo::Timestamp time)
{
    // 使用id和password进行登录业务
    int id = js["id"].get<int>();
    std::string password = js["password"];

    // 从数据库中查找比对是否有对应的用户
    User user = _userModel.query(id);
    // Id 不等于-1说明 有这个id的用户，如果password也匹配上了那么就登录成功
    if (user.getId() != -1 && user.getPassword() == password)
    {
        // “挤号了”
        if (user.getState() == "online")
        {
            json js;
            js["error"] = 2;
            js["errorMeg"] = "该账号已经登录";
            js["return Type"] = LOGIN_MSG_ACK;
            conn->send(js.dump());
        }
        else if (user.getState() == "offline")
        {
            // 登录成功

            { // 操作connnection map有线程安全问题，所以我们需要加锁
                std::lock_guard<std::mutex> lock(_mutex);
                _userConnectionMap.insert(std::make_pair(id, conn));
            }
            user.setState("online");
            _userModel.updateState(user);

            json js;
            js["error"] = 0;
            js["return Type"] = LOGIN_MSG_ACK;
            js["LoginMeg"] = "您已经登陆成功";

            // 登录成功，就查询一下是否有离线信息，有的话就返回回去
            // 服务器中转离线消息，原来是上限后发给他的
            std::vector<std::string> offlineMsg;
            offlineMsg = _offMsgModel.query(id);

            if (!offlineMsg.empty())
            {
                js["OfflineMessage"] = offlineMsg;
                _offMsgModel.remove(id);
            }

            // 在用户登录后把他的好友的信息返回给他
            std::vector<User> FriendMsg = _friendModel.query(id);
            if(!FriendMsg.empty())
                js["FriendMsg"] = FriendMsg;

            conn->send(js.dump());
        }
    }
    else
    {
        json js;
        js["error"] = 1;
        js["return Type"] = LOGIN_MSG_ACK;
        js["LoginMeg"] = "账号或密码错误";
        js["id"] = user.getId();
        js["name"] = user.getName();
        conn->send(js.dump());
    }
}

void ChatService::Register(const muduo::net::TcpConnectionPtr &conn, json &js, muduo::Timestamp time)
{
    // 约定 ：注册业务，对端必须发来对应的name和password,还有标识业务方法的messageid
    std::string name = js["name"];
    std::string password = js["password"];
    std::string state = "offline";

    User user;
    user.setName(name);
    user.setPassword(password);

    // 调用成功，返回这个用户的id，将返回标识设为1（因为返回的信息不一定执行成功了，所以一定要一个返回标识）
    json responce;
    if (_userModel.insert(user))
    {
        responce["id"] = user.getId();
        responce["error"] = 0;
        responce["return Type"] = REG_MSG_ACK;

        conn->send(responce.dump());
    }
    else
    {
        responce["error"] = 1;
        responce["errorMessaege"] = "The business of register failed";
        responce["return Type"] = REG_MSG_ACK;

        conn->send(responce.dump());
    }
}

void ChatService::ClinetConnetionException(const muduo::net::TcpConnectionPtr &conn)
{
    // 发生了链接异常问题，将该conn从map中移除，然后修改数据库

    User user;

    {
        std::lock_guard<std::mutex> lock(_mutex);
        for (auto it = _userConnectionMap.begin(); it != _userConnectionMap.end(); ++it)
        {
            if (it->second == conn)
            {
                user.setId(it->first);
                _userConnectionMap.erase(it);

                break;
            }
        }
    }

    user.setState("offline");
    _userModel.updateState(user);
}

//注意了这里还要大改，因为 用户 可能在其他的服务器上登录了，这就叫服务器集群
void ChatService::OneChat(const muduo::net::TcpConnectionPtr &conn, json &js, muduo::Timestamp time)
{
    // json : msgId, id , from(发起者的名字) , to , meg
    int toid = js["to"].get<int>();

    {
        std::lock_guard<std::mutex> lock(_mutex);
        auto it = _userConnectionMap.find(toid);
        if (it != _userConnectionMap.end())
        {
            // 服务器相当于做一个中间的转发
            it->second->send(js.dump());
            return;
        }
    }

    // 发生目标不在线,离线存储业务
    
    //传右值，对端不可以使用引用
    _offMsgModel.insert(toid, js.dump());

    //传左值，对端可以使用引用接收
    // 这里 要保证sendJs的声明周期比 “引用参数”要长
    // std::string sendJs = js.dump();
    // _offMsgModel.insert(toid,sendJs);
}


//为何不在main函数那里定义UserModel类，然后操作UserModel的函数 ，而是在这里调用UserModel类类内函数
//（因为这样可以降低耦合度，不让main函数包含UserModel头文件，只让ChatService包含头文件）
void ChatService::reset()
{
     _userModel.resetState();
}

void ChatService::AddFriend(const muduo::net::TcpConnectionPtr &conn,  json& js, muduo::Timestamp time)
{
     int userid = js["id"].get<int>() , friendid = js["friendid"].get<int>();

     _friendModel.insert(userid,friendid);
}